--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
scripts/rpi/install_meshchatx.sh 65ca9ddc823fcc2c8f8653c553b944b85db0875f (65ca9ddc) Text, 24.99 KB
T8b949e#!/usr/bin/env bash
Tffa657set -euo pipefail
T8b949e# Official defaults. Override for forks or mirrors:
T8b949e# MESHCHATX_RELEASES_RSS Release feed (default: .../MeshChatX/releases.rss)
T8b949e# MESHCHATX_REPO_BASE Repo root for synthesized wheel URLs if RSS has no
T8b949e# .whl link in descriptions (default: derived from RSS URL)
T8b949e# Cosign: Sigstore attestation verify needs the real cosign binary. This script can
T8b949e# download a checksum-verified release from GitHub to /tmp if none is on PATH.
T8b949e# MESHCHATX_COSIGN_VERSION (default: 3.0.6)
T8b949e# MESHCHATX_COSIGN_PUB_URL (default: raw cosign.pub from master in this repo)
Te6edf3RUN_USERTff7b72=Ta5d6ff"Tffd700${Te6edf3SUDO_USERTff7b72:-Te6edf3$USERTffd700}Ta5d6ff"
Te6edf3RUN_GROUPTff7b72=Ta5d6ff"Tff7b72$(id -gn Ta5d6ff"Te6edf3$RUN_USERTa5d6ff"Tff7b72)Ta5d6ff"
Te6edf3USER_HOMETff7b72=Ta5d6ff"Tff7b72$(Tffa657eval Tffa657echo Ta5d6ff"Ta5d6ff~Tffd700${Te6edf3RUN_USERTffd700}Ta5d6ff"Tff7b72)Ta5d6ff"
Tff7b72if Tff7b72[Tff7b72[ -t T79c0ff1 Tff7b72]Tff7b72] Tff7b72&& Tffa657command -v tput >/dev/null T79c0ff2>Tb4b4b4&T79c0ff1Tb4b4b4; Tff7b72then
Te6edf3C_RESETTff7b72=Ta5d6ff"Tff7b72$(tput sgr0Tff7b72)Ta5d6ff"
Te6edf3C_BOLDTff7b72=Ta5d6ff"Tff7b72$(tput boldTff7b72)Ta5d6ff"
Te6edf3C_REDTff7b72=Ta5d6ff"Tff7b72$(tput setaf T79c0ff1Tff7b72)Ta5d6ff"
Te6edf3C_GREENTff7b72=Ta5d6ff"Tff7b72$(tput setaf T79c0ff2Tff7b72)Ta5d6ff"
Te6edf3C_YELLOWTff7b72=Ta5d6ff"Tff7b72$(tput setaf T79c0ff3Tff7b72)Ta5d6ff"
Te6edf3C_BLUETff7b72=Ta5d6ff"Tff7b72$(tput setaf T79c0ff4Tff7b72)Ta5d6ff"
Tff7b72else
Te6edf3C_RESETTff7b72=Ta5d6ff""
Te6edf3C_BOLDTff7b72=Ta5d6ff""
Te6edf3C_REDTff7b72=Ta5d6ff""
Te6edf3C_GREENTff7b72=Ta5d6ff""
Te6edf3C_YELLOWTff7b72=Ta5d6ff""
Te6edf3C_BLUETff7b72=Ta5d6ff""
Tff7b72fi
noteTff7b72(Tff7b72) Tff7b72{
Tffa657echo Ta5d6ff"Tffd700${Te6edf3C_BLUETffd700}Tffd700${Te6edf3C_BOLDTffd700}Ta5d6ff==>Tffd700${Te6edf3C_RESETTffd700}Ta5d6ff Te6edf3$*Ta5d6ff"
Tff7b72}
warnTff7b72(Tff7b72) Tff7b72{
Tffa657echo Ta5d6ff"Tffd700${Te6edf3C_YELLOWTffd700}Tffd700${Te6edf3C_BOLDTffd700}Ta5d6ffWARN:Tffd700${Te6edf3C_RESETTffd700}Ta5d6ff Te6edf3$*Ta5d6ff"
Tff7b72}
errTff7b72(Tff7b72) Tff7b72{
Tffa657echo Ta5d6ff"Tffd700${Te6edf3C_REDTffd700}Tffd700${Te6edf3C_BOLDTffd700}Ta5d6ffERROR:Tffd700${Te6edf3C_RESETTffd700}Ta5d6ff Te6edf3$*Ta5d6ff" >Tb4b4b4&T79c0ff2
Tff7b72}
okTff7b72(Tff7b72) Tff7b72{
Tffa657echo Ta5d6ff"Tffd700${Te6edf3C_GREENTffd700}Tffd700${Te6edf3C_BOLDTffd700}Ta5d6ffOK:Tffd700${Te6edf3C_RESETTffd700}Ta5d6ff Te6edf3$*Ta5d6ff"
Tff7b72}
run_as_userTff7b72(Tff7b72) Tff7b72{
Tffa657local Te6edf3cmdTff7b72=Ta5d6ff"Te6edf3$1Ta5d6ff"
Tff7b72if Tff7b72[Tff7b72[ Ta5d6ff"Te6edf3$EUIDTa5d6ff" -eq T79c0ff0 Tff7b72&& Ta5d6ff"Te6edf3$RUN_USERTa5d6ff" !Tff7b72= Ta5d6ff"root" Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
sudo -u Ta5d6ff"Te6edf3$RUN_USERTa5d6ff" -H bash -lc Ta5d6ff"Te6edf3$cmdTa5d6ff"
Tff7b72else
bash -lc Ta5d6ff"Te6edf3$cmdTa5d6ff"
Tff7b72fi
Tff7b72}
prompt_defaultTff7b72(Tff7b72) Tff7b72{
Tffa657local Te6edf3promptTff7b72=Ta5d6ff"Te6edf3$1Ta5d6ff"
Tffa657local Te6edf3defaultTff7b72=Ta5d6ff"Te6edf3$2Ta5d6ff"
Tffa657local Te6edf3valueTff7b72=Ta5d6ff""
Tffa657read -r -p Ta5d6ff"Te6edf3$promptTa5d6ff [Te6edf3$defaultTa5d6ff]: Ta5d6ff" value
Tff7b72if Tff7b72[Tff7b72[ -z Ta5d6ff"Te6edf3$valueTa5d6ff" Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
Te6edf3valueTff7b72=Ta5d6ff"Te6edf3$defaultTa5d6ff"
Tff7b72fi
Tffa657echo Ta5d6ff"Te6edf3$valueTa5d6ff"
Tff7b72}
prompt_yes_noTff7b72(Tff7b72) Tff7b72{
Tffa657local Te6edf3promptTff7b72=Ta5d6ff"Te6edf3$1Ta5d6ff"
Tffa657local Te6edf3defaultTff7b72=Ta5d6ff"Tffd700${Te6edf32Tff7b72:-Te6edf3yTffd700}Ta5d6ff"
Tffa657local Te6edf3answerTff7b72=Ta5d6ff""
Tffa657local Te6edf3hintTff7b72=Ta5d6ff"Y/n"
Tff7b72if Tff7b72[Tff7b72[ Ta5d6ff"Te6edf3$defaultTa5d6ff" Tff7b72=Tff7b72= Ta5d6ff"n" Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
Te6edf3hintTff7b72=Ta5d6ff"y/N"
Tff7b72fi
Tff7b72while trueTb4b4b4; Tff7b72do
Tffa657read -r -p Ta5d6ff"Te6edf3$promptTa5d6ff (Te6edf3$hintTa5d6ff): Ta5d6ff" answer
Tff7b72if Tff7b72[Tff7b72[ -z Ta5d6ff"Te6edf3$answerTa5d6ff" Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
Te6edf3answerTff7b72=Ta5d6ff"Te6edf3$defaultTa5d6ff"
Tff7b72fi
Tff7b72case Ta5d6ff"Tffd700${Te6edf3answerTb4b4b4,,Tffd700}Ta5d6ff" Tff7b72in
yTb4b4b4|yesTff7b72) Tff7b72return T79c0ff0 Tb4b4b4;Tb4b4b4;
nTb4b4b4|noTff7b72) Tff7b72return T79c0ff1 Tb4b4b4;Tb4b4b4;
*Tff7b72)
Tffa657echo Ta5d6ff"Please answer y or n."
Tb4b4b4;Tb4b4b4;
Tff7b72esac
Tff7b72done
Tff7b72}
repo_base_from_rssTff7b72(Tff7b72) Tff7b72{
Tffa657local Te6edf3uTff7b72=Ta5d6ff"Te6edf3$1Ta5d6ff"
Tff7b72case Ta5d6ff"Te6edf3$uTa5d6ff" Tff7b72in
*Ta5d6ff"/releases.rss"Tff7b72)
Tffa657echo Ta5d6ff"Tffd700${Te6edf3uTb4b4b4%/releases.rssTffd700}Ta5d6ff"
Tb4b4b4;Tb4b4b4;
*Ta5d6ff"/releases.atom"Tff7b72)
Tffa657echo Ta5d6ff"Tffd700${Te6edf3uTb4b4b4%/releases.atomTffd700}Ta5d6ff"
Tb4b4b4;Tb4b4b4;
*Tff7b72)
Tffa657echo Ta5d6ff"Tffd700${Te6edf3uTb4b4b4%/*Tffd700}Ta5d6ff"
Tb4b4b4;Tb4b4b4;
Tff7b72esac
Tff7b72}
is_prerelease_tagTff7b72(Tff7b72) Tff7b72{
Tffa657local Te6edf3tTff7b72=Ta5d6ff"Tffd700${Te6edf31Tb4b4b4#vTffd700}Ta5d6ff"
Te6edf3tTff7b72=Ta5d6ff"Tffd700${Te6edf3tTb4b4b4#VTffd700}Ta5d6ff"
Tff7b72if Tff7b72[Tff7b72[ Ta5d6ff"Te6edf3$tTa5d6ff" Tff7b72=~ Tff7b72(^Tb4b4b4|Tff7b72[-_.Tff7b72]Tff7b72)Tff7b72(rcTb4b4b4|RCTb4b4b4|alphaTb4b4b4|betaTb4b4b4|preTb4b4b4|devTb4b4b4|aTff7b72[T79c0ff0-9Tff7b72]+Tb4b4b4|bTff7b72[T79c0ff0-9Tff7b72]+Tff7b72)Tff7b72(Tff7b72[-_.Tff7b72[:digit:Tff7b72]Tff7b72]Tb4b4b4|$Tff7b72) Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
Tff7b72return T79c0ff0
Tff7b72fi
Tff7b72return T79c0ff1
Tff7b72}
absolutize_linkTff7b72(Tff7b72) Tff7b72{
Tffa657local Te6edf3lTff7b72=Ta5d6ff"Te6edf3$1Ta5d6ff" Te6edf3oTff7b72=Ta5d6ff"Te6edf3$2Ta5d6ff"
Tff7b72case Ta5d6ff"Te6edf3$lTa5d6ff" Tff7b72in
http://* Tb4b4b4| https://*Tff7b72) Tffa657printf Ta5d6ff"%s" Ta5d6ff"Te6edf3$lTa5d6ff" Tb4b4b4;Tb4b4b4;
//*Tff7b72)
Tff7b72case Ta5d6ff"Te6edf3$oTa5d6ff" Tff7b72in
http://*Tff7b72) Tffa657printf Ta5d6ff"http:%s" Ta5d6ff"Te6edf3$lTa5d6ff" Tb4b4b4;Tb4b4b4;
https://*Tff7b72) Tffa657printf Ta5d6ff"https:%s" Ta5d6ff"Te6edf3$lTa5d6ff" Tb4b4b4;Tb4b4b4;
*Tff7b72) Tffa657printf Ta5d6ff"https:%s" Ta5d6ff"Te6edf3$lTa5d6ff" Tb4b4b4;Tb4b4b4;
Tff7b72esac
Tb4b4b4;Tb4b4b4;
/*Tff7b72)
Tffa657local Te6edf3hTff7b72=Ta5d6ff"Tffd700${Te6edf3oTb4b4b4#*//Tffd700}Ta5d6ff"
Te6edf3hTff7b72=Ta5d6ff"Tffd700${Te6edf3hTb4b4b4%%/*Tffd700}Ta5d6ff"
Tff7b72case Ta5d6ff"Te6edf3$oTa5d6ff" Tff7b72in
http://*Tff7b72) Tffa657printf Ta5d6ff"http://%s%s" Ta5d6ff"Te6edf3$hTa5d6ff" Ta5d6ff"Te6edf3$lTa5d6ff" Tb4b4b4;Tb4b4b4;
https://*Tff7b72) Tffa657printf Ta5d6ff"https://%s%s" Ta5d6ff"Te6edf3$hTa5d6ff" Ta5d6ff"Te6edf3$lTa5d6ff" Tb4b4b4;Tb4b4b4;
*Tff7b72) Tffa657printf Ta5d6ff"https://%s%s" Ta5d6ff"Te6edf3$hTa5d6ff" Ta5d6ff"Te6edf3$lTa5d6ff" Tb4b4b4;Tb4b4b4;
Tff7b72esac
Tb4b4b4;Tb4b4b4;
*Tff7b72) Tffa657printf Ta5d6ff"%s" Ta5d6ff"Te6edf3$lTa5d6ff" Tb4b4b4;Tb4b4b4;
Tff7b72esac
Tff7b72}
tag_from_linkTff7b72(Tff7b72) Tff7b72{
Tffa657local Te6edf3lTff7b72=Ta5d6ff"Te6edf3$1Ta5d6ff"
Tff7b72if Tff7b72[Tff7b72[ Ta5d6ff"Te6edf3$lTa5d6ff" Tff7b72=~ /releases/tag/Tff7b72(Tff7b72[^/?#Tff7b72]+Tff7b72) Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
Tffa657echo Ta5d6ff"Tffd700${Te6edf3BASH_REMATCHTb4b4b4[1]Tffd700}Ta5d6ff"
Tff7b72fi
Tff7b72}
discover_release_wheelsTff7b72(Tff7b72) Tff7b72{
Tffa657local rss raw repo_base st_tag st_url p_tag p_url
Tff7b72if ! Tffa657command -v curl >/dev/null T79c0ff2>Tb4b4b4&T79c0ff1Tb4b4b4; Tff7b72then
err Ta5d6ff"curl is required (RSS and downloads)."
Tff7b72return T79c0ff1
Tff7b72fi
Te6edf3MESHCHATX_RELEASES_RSSTff7b72=Ta5d6ff"Tffd700${Te6edf3MESHCHATX_RELEASES_RSSTff7b72:-Te6edf3httpsTb4b4b4:Tb4b4b4//github.com/Quad4-Software/MeshChatX/releases.rssTffd700}Ta5d6ff"
Te6edf3repo_baseTff7b72=Ta5d6ff"Tffd700${Te6edf3MESHCHATX_REPO_BASETff7b72:-Tffd700}Ta5d6ff"
Te6edf3rssTff7b72=Ta5d6ff"Te6edf3$MESHCHATX_RELEASES_RSSTa5d6ff"
Tff7b72[Tff7b72[ -n Ta5d6ff"Te6edf3$repo_baseTa5d6ff" Tff7b72]Tff7b72] Tff7b72|| Te6edf3repo_baseTff7b72=Ta5d6ff"Tff7b72$(repo_base_from_rss Ta5d6ff"Te6edf3$rssTa5d6ff"Tff7b72)Ta5d6ff"
Tff7b72if ! Te6edf3rawTff7b72=Ta5d6ff"Tff7b72$(
curl -fsSL -m T79c0ff90 -H Ta5d6ff"User-Agent: MeshChatX-rpi-installer/1 (+https://github.com/Quad4-Software/MeshChatX)" Tffea00\
Ta5d6ff"Te6edf3$rssTa5d6ff" T79c0ff2>/dev/null
Tff7b72)Ta5d6ff"Tb4b4b4; Tff7b72then
Tff7b72return T79c0ff1
Tff7b72fi
Tff7b72[Tff7b72[ -n Ta5d6ff"Te6edf3$rawTa5d6ff" Tff7b72]Tff7b72] Tff7b72|| Tff7b72return T79c0ff1
Te6edf3st_tagTff7b72=Ta5d6ff""Tb4b4b4; Te6edf3st_urlTff7b72=Ta5d6ff""Tb4b4b4; Te6edf3p_tagTff7b72=Ta5d6ff""Tb4b4b4; Te6edf3p_urlTff7b72=Ta5d6ff""
Tff7b72while Te6edf3IFSTff7b72=Ta5d6ff$'\t' Tffa657read -r alink itemblk Tff7b72|| Tff7b72[Tff7b72[ -n Ta5d6ff"Te6edf3$alinkTa5d6ff" Tff7b72]Tff7b72]Tb4b4b4; Tff7b72do
Tff7b72[Tff7b72[ -z Ta5d6ff"Te6edf3$alinkTa5d6ff" Tff7b72&& -z Ta5d6ff"Te6edf3$itemblkTa5d6ff" Tff7b72]Tff7b72] Tff7b72&& Tff7b72continue
Tffa657local link t ver synth whl
Te6edf3linkTff7b72=Ta5d6ff"Tff7b72$(absolutize_link Ta5d6ff"Te6edf3$alinkTa5d6ff" Ta5d6ff"Te6edf3$rssTa5d6ff"Tff7b72)Ta5d6ff"
Te6edf3tTff7b72=Ta5d6ff"Tff7b72$(tag_from_link Ta5d6ff"Te6edf3$linkTa5d6ff"Tff7b72)Ta5d6ff"
Tff7b72[Tff7b72[ -n Ta5d6ff"Te6edf3$tTa5d6ff" Tff7b72]Tff7b72] Tff7b72|| Tff7b72continue
Te6edf3whlTff7b72=Ta5d6ff"Tff7b72$(
Tffa657printf Ta5d6ff"%s" Ta5d6ff"Te6edf3$itemblkTa5d6ff" Tb4b4b4| tr -d Ta5d6ff'\r' Tb4b4b4| awk Ta5d6ff'{
s = $0
while (match(s, /https?:\/\/[^[:space:]<&]+\.whl/)) {
w = substr(s, RSTART, RLENGTH)
if (w ~ /[Mm]eshchatx/) { print w; exit 0 }
s = substr(s, RSTART + 1)
}
s = $0
while (match(s, /https?:\/\/[^[:space:]<&]+\.whl/)) {
print substr(s, RSTART, RLENGTH)
exit 0
}
}'
Tff7b72)Ta5d6ff"
Te6edf3verTff7b72=Ta5d6ff"Te6edf3$tTa5d6ff"
Tff7b72if Tff7b72[Tff7b72[ Ta5d6ff"Te6edf3$verTa5d6ff" Tff7b72=Tff7b72= v* Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
Te6edf3verTff7b72=Ta5d6ff"Tffd700${Te6edf3verTb4b4b4#vTffd700}Ta5d6ff"
Tff7b72elif Tff7b72[Tff7b72[ Ta5d6ff"Te6edf3$verTa5d6ff" Tff7b72=Tff7b72= V* Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
Te6edf3verTff7b72=Ta5d6ff"Tffd700${Te6edf3verTb4b4b4#VTffd700}Ta5d6ff"
Tff7b72fi
Te6edf3synthTff7b72=Ta5d6ff"Tffd700${Te6edf3repo_baseTffd700}Ta5d6ff/releases/download/Tffd700${Te6edf3tTffd700}Ta5d6ff/reticulum_meshchatx-Tffd700${Te6edf3verTffd700}Ta5d6ff-py3-none-any.whlTa5d6ff"
Tff7b72if Tff7b72[Tff7b72[ -n Ta5d6ff"Te6edf3$whlTa5d6ff" Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
Te6edf3linkTff7b72=Ta5d6ff"Te6edf3$whlTa5d6ff"
Tff7b72else
Te6edf3linkTff7b72=Ta5d6ff"Te6edf3$synthTa5d6ff"
Tff7b72fi
Tff7b72if is_prerelease_tag Ta5d6ff"Te6edf3$tTa5d6ff"Tb4b4b4; Tff7b72then
Tff7b72if Tff7b72[Tff7b72[ -z Ta5d6ff"Te6edf3$p_urlTa5d6ff" Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
Te6edf3p_tagTff7b72=Ta5d6ff"Te6edf3$tTa5d6ff"
Te6edf3p_urlTff7b72=Ta5d6ff"Te6edf3$linkTa5d6ff"
Tff7b72fi
Tff7b72else
Tff7b72if Tff7b72[Tff7b72[ -z Ta5d6ff"Te6edf3$st_urlTa5d6ff" Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
Te6edf3st_tagTff7b72=Ta5d6ff"Te6edf3$tTa5d6ff"
Te6edf3st_urlTff7b72=Ta5d6ff"Te6edf3$linkTa5d6ff"
Tff7b72fi
Tff7b72fi
Tff7b72done < <Tff7b72(
Tffa657printf Ta5d6ff'%s' Ta5d6ff"Te6edf3$rawTa5d6ff" Tb4b4b4| tr -d Ta5d6ff'\r' Tb4b4b4| awk Ta5d6ff'BEGIN{RS="<item>";} NR>1 {
blk=$0
gsub(/<atom:link/,"<link",blk);
p=index(blk, "<link>");
if (!p) next;
s=substr(blk, p+6);
q=index(s, "</link>");
if (!q) next;
l=substr(s, 1, q-1);
gsub(/^[ \t\n]+/,"",l);
gsub(/[ \t\n]+$/,"",l);
print l "\t" blk
}'
Tff7b72)
Tffa657printf Ta5d6ff'%s\n' Ta5d6ff"Te6edf3$st_tagTa5d6ff" Ta5d6ff"Te6edf3$st_urlTa5d6ff" Ta5d6ff"Te6edf3$p_tagTa5d6ff" Ta5d6ff"Te6edf3$p_urlTa5d6ff"
Tff7b72}
prompt_wheel_sourceTff7b72(Tff7b72) Tff7b72{
Tffa657local Te6edf3stable_tagTff7b72=Ta5d6ff"Te6edf3$1Ta5d6ff"
Tffa657local Te6edf3stable_urlTff7b72=Ta5d6ff"Te6edf3$2Ta5d6ff"
Tffa657local Te6edf3pre_tagTff7b72=Ta5d6ff"Te6edf3$3Ta5d6ff"
Tffa657local Te6edf3pre_urlTff7b72=Ta5d6ff"Te6edf3$4Ta5d6ff"
Tffa657local Te6edf3choiceTff7b72=Ta5d6ff"" Te6edf3default_choiceTff7b72=Ta5d6ff"1"
Tffa657echo
note Ta5d6ff"Pick the MeshChatX wheel (from Gitea release feed)."
Tff7b72if Tff7b72[Tff7b72[ -n Ta5d6ff"Te6edf3$stable_tagTa5d6ff" Tff7b72&& -n Ta5d6ff"Te6edf3$stable_urlTa5d6ff" Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
Tffa657echo Ta5d6ff"Ta5d6ff 1) stable (Tffd700${Te6edf3stable_tagTffd700}Ta5d6ff)Ta5d6ff"
Tff7b72else
Tffa657echo Ta5d6ff" 1) stable (not available from feed)"
Te6edf3default_choiceTff7b72=Ta5d6ff"3"
Tff7b72fi
Tff7b72if Tff7b72[Tff7b72[ -n Ta5d6ff"Te6edf3$pre_tagTa5d6ff" Tff7b72&& -n Ta5d6ff"Te6edf3$pre_urlTa5d6ff" Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
Tffa657echo Ta5d6ff"Ta5d6ff 2) pre-release (Tffd700${Te6edf3pre_tagTffd700}Ta5d6ff)Ta5d6ff"
Tff7b72else
Tffa657echo Ta5d6ff" 2) pre-release (not available from feed)"
Tff7b72fi
Tffa657echo Ta5d6ff" 3) custom wheel URL"
Tff7b72if Tff7b72[Tff7b72[ -z Ta5d6ff"Te6edf3$stable_urlTa5d6ff" Tff7b72&& -z Ta5d6ff"Te6edf3$pre_urlTa5d6ff" Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
Te6edf3default_choiceTff7b72=Ta5d6ff"3"
Tff7b72elif Tff7b72[Tff7b72[ -z Ta5d6ff"Te6edf3$stable_urlTa5d6ff" Tff7b72&& -n Ta5d6ff"Te6edf3$pre_urlTa5d6ff" Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
Te6edf3default_choiceTff7b72=Ta5d6ff"2"
Tff7b72fi
Tff7b72while trueTb4b4b4; Tff7b72do
Tffa657read -r -p Ta5d6ff"Ta5d6ffSelection [1/2/3] (default Tffd700${Te6edf3default_choiceTffd700}Ta5d6ff): Ta5d6ff" choice
Tff7b72if Tff7b72[Tff7b72[ -z Ta5d6ff"Te6edf3$choiceTa5d6ff" Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
Te6edf3choiceTff7b72=Ta5d6ff"Te6edf3$default_choiceTa5d6ff"
Tff7b72fi
Tff7b72case Ta5d6ff"Te6edf3$choiceTa5d6ff" Tff7b72in
T79c0ff1Tff7b72)
Tff7b72if Tff7b72[Tff7b72[ -n Ta5d6ff"Te6edf3$stable_urlTa5d6ff" Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
Tffa657echo Ta5d6ff"Te6edf3$stable_urlTa5d6ff"
Tff7b72return T79c0ff0
Tff7b72fi
Tffa657echo Ta5d6ff"Stable wheel is not available; choose 2 or 3." >Tb4b4b4&T79c0ff2
Tb4b4b4;Tb4b4b4;
T79c0ff2Tff7b72)
Tff7b72if Tff7b72[Tff7b72[ -n Ta5d6ff"Te6edf3$pre_urlTa5d6ff" Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
Tffa657echo Ta5d6ff"Te6edf3$pre_urlTa5d6ff"
Tff7b72return T79c0ff0
Tff7b72fi
Tffa657echo Ta5d6ff"Pre-release wheel is not available; choose 1 or 3." >Tb4b4b4&T79c0ff2
Tb4b4b4;Tb4b4b4;
T79c0ff3Tff7b72)
Tffa657local Te6edf3custom_uTff7b72=Ta5d6ff""
Te6edf3custom_uTff7b72=Ta5d6ff"Tff7b72$(prompt_default Ta5d6ff"Wheel URL" Ta5d6ff""Tff7b72)Ta5d6ff"
Tff7b72if Tff7b72[Tff7b72[ -z Ta5d6ff"Te6edf3$custom_uTa5d6ff" Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
Tffa657echo Ta5d6ff"URL cannot be empty." >Tb4b4b4&T79c0ff2
Tff7b72else
Tffa657echo Ta5d6ff"Te6edf3$custom_uTa5d6ff"
Tff7b72return T79c0ff0
Tff7b72fi
Tb4b4b4;Tb4b4b4;
*Tff7b72)
Tffa657echo Ta5d6ff"Please enter 1, 2, or 3." >Tb4b4b4&T79c0ff2
Tb4b4b4;Tb4b4b4;
Tff7b72esac
Tff7b72done
Tff7b72}
pick_package_managerTff7b72(Tff7b72) Tff7b72{
Tff7b72if Tffa657command -v apt-get >/dev/null T79c0ff2>Tb4b4b4&T79c0ff1Tb4b4b4; Tff7b72then
Tffa657echo Ta5d6ff"apt"
Tff7b72return
Tff7b72fi
Tff7b72if Tffa657command -v dnf >/dev/null T79c0ff2>Tb4b4b4&T79c0ff1Tb4b4b4; Tff7b72then
Tffa657echo Ta5d6ff"dnf"
Tff7b72return
Tff7b72fi
Tff7b72if Tffa657command -v pacman >/dev/null T79c0ff2>Tb4b4b4&T79c0ff1Tb4b4b4; Tff7b72then
Tffa657echo Ta5d6ff"pacman"
Tff7b72return
Tff7b72fi
Tffa657echo Ta5d6ff"none"
Tff7b72}
install_package_if_possibleTff7b72(Tff7b72) Tff7b72{
Tffa657local Te6edf3packageTff7b72=Ta5d6ff"Te6edf3$1Ta5d6ff"
Tffa657local mgr
Te6edf3mgrTff7b72=Ta5d6ff"Tff7b72$(pick_package_managerTff7b72)Ta5d6ff"
Tff7b72case Ta5d6ff"Te6edf3$mgrTa5d6ff" Tff7b72in
aptTff7b72)
Tff7b72if Tff7b72[Tff7b72[ Ta5d6ff"Te6edf3$EUIDTa5d6ff" -eq T79c0ff0 Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
apt-get update Tff7b72&& apt-get install -y Ta5d6ff"Te6edf3$packageTa5d6ff"
Tff7b72else
sudo apt-get update Tff7b72&& sudo apt-get install -y Ta5d6ff"Te6edf3$packageTa5d6ff"
Tff7b72fi
Tb4b4b4;Tb4b4b4;
dnfTff7b72)
Tff7b72if Tff7b72[Tff7b72[ Ta5d6ff"Te6edf3$EUIDTa5d6ff" -eq T79c0ff0 Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
dnf install -y Ta5d6ff"Te6edf3$packageTa5d6ff"
Tff7b72else
sudo dnf install -y Ta5d6ff"Te6edf3$packageTa5d6ff"
Tff7b72fi
Tb4b4b4;Tb4b4b4;
pacmanTff7b72)
Tff7b72if Tff7b72[Tff7b72[ Ta5d6ff"Te6edf3$EUIDTa5d6ff" -eq T79c0ff0 Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
pacman -Sy --noconfirm Ta5d6ff"Te6edf3$packageTa5d6ff"
Tff7b72else
sudo pacman -Sy --noconfirm Ta5d6ff"Te6edf3$packageTa5d6ff"
Tff7b72fi
Tb4b4b4;Tb4b4b4;
noneTff7b72)
warn Ta5d6ff"Ta5d6ffNo supported package manager found (apt/dnf/pacman). Skipping install for Te6edf3$packageTa5d6ff.Ta5d6ff"
Tb4b4b4;Tb4b4b4;
Tff7b72esac
Tff7b72}
check_port_availableTff7b72(Tff7b72) Tff7b72{
Tffa657local Te6edf3portTff7b72=Ta5d6ff"Te6edf3$1Ta5d6ff"
Tff7b72if Tffa657command -v ss >/dev/null T79c0ff2>Tb4b4b4&T79c0ff1Tb4b4b4; Tff7b72then
Tff7b72if ss -tlnH T79c0ff2>/dev/null Tb4b4b4| grep -qE Ta5d6ff"Ta5d6ff:Tffd700${Te6edf3portTffd700}Ta5d6ff( |$Ta5d6ff)Ta5d6ff"Tb4b4b4; Tff7b72then
Tff7b72return T79c0ff1
Tff7b72fi
Tff7b72return T79c0ff0
Tff7b72fi
Tff7b72if Tffa657command -v netstat >/dev/null T79c0ff2>Tb4b4b4&T79c0ff1Tb4b4b4; Tff7b72then
Tff7b72if netstat -tln T79c0ff2>/dev/null Tb4b4b4| grep -qE Ta5d6ff"Ta5d6ff:Tffd700${Te6edf3portTffd700}Ta5d6ff( |$Ta5d6ff)Ta5d6ff"Tb4b4b4; Tff7b72then
Tff7b72return T79c0ff1
Tff7b72fi
Tff7b72return T79c0ff0
Tff7b72fi
warn Ta5d6ff"ss/netstat not found; port availability not checked."
Tff7b72return T79c0ff0
Tff7b72}
detect_archTff7b72(Tff7b72) Tff7b72{
uname -m Tb4b4b4| tr Ta5d6ff'[:upper:]' Ta5d6ff'[:lower:]'
Tff7b72}
is_supported_rpi_archTff7b72(Tff7b72) Tff7b72{
Tffa657local Te6edf3archTff7b72=Ta5d6ff"Te6edf3$1Ta5d6ff"
Tff7b72case Ta5d6ff"Te6edf3$archTa5d6ff" Tff7b72in
armv6lTb4b4b4|armv7lTb4b4b4|aarch64Tb4b4b4|arm64Tff7b72)
Tff7b72return T79c0ff0
Tb4b4b4;Tb4b4b4;
*Tff7b72)
Tff7b72return T79c0ff1
Tb4b4b4;Tb4b4b4;
Tff7b72esac
Tff7b72}
write_system_serviceTff7b72(Tff7b72) Tff7b72{
Tffa657local Te6edf3exec_cmdTff7b72=Ta5d6ff"Te6edf3$1Ta5d6ff"
Tffa657local Te6edf3workdirTff7b72=Ta5d6ff"Te6edf3$2Ta5d6ff"
Tffa657local Te6edf3path_valueTff7b72=Ta5d6ff"Te6edf3$3Ta5d6ff"
Tffa657local Te6edf3svcTff7b72=Ta5d6ff"/etc/systemd/system/meshchatx.service"
Tff7b72if Tff7b72[Tff7b72[ Ta5d6ff"Te6edf3$EUIDTa5d6ff" -eq T79c0ff0 Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
Te6edf3SUDOTff7b72=Ta5d6ff""
Tff7b72else
Te6edf3SUDOTff7b72=Ta5d6ff"sudo"
Tff7b72fi
Te6edf3$SUDO tee Ta5d6ff"Te6edf3$svcTa5d6ff" >/dev/null Ta5d6ff<<EOF
[Unit]
Description=MeshChatX Headless (system service)
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=${RUN_USER}
Group=${RUN_GROUP}
WorkingDirectory=${workdir}
Environment="PATH=${path_value}"
ExecStart=${exec_cmd}
Restart=always
RestartSec=3
[Install]
WantedBy=multi-user.target
EOF
Tff7b72}
write_user_serviceTff7b72(Tff7b72) Tff7b72{
Tffa657local Te6edf3exec_cmdTff7b72=Ta5d6ff"Te6edf3$1Ta5d6ff"
Tffa657local Te6edf3workdirTff7b72=Ta5d6ff"Te6edf3$2Ta5d6ff"
Tffa657local Te6edf3path_valueTff7b72=Ta5d6ff"Te6edf3$3Ta5d6ff"
Tffa657local Te6edf3svc_pathTff7b72=Ta5d6ff"Tffd700${Te6edf3USER_HOMETffd700}Ta5d6ff/.config/systemd/user/meshchatx.serviceTa5d6ff"
run_as_user Ta5d6ff"Ta5d6ffmkdir -p 'Tffd700${Te6edf3USER_HOMETffd700}Ta5d6ff/.config/systemd/user'Ta5d6ff"
run_as_user Ta5d6ff"Ta5d6ffcat > 'Tffd700${Te6edf3svc_pathTffd700}Ta5d6ff' <<'EOF'
[Unit]
Description=MeshChatX Headless (user service)
After=network-online.target
[Service]
Type=simple
WorkingDirectory=Tffd700${Te6edf3workdirTffd700}
Ta5d6ffEnvironment=\"PATH=Tffd700${Te6edf3path_valueTffd700}Ta5d6ff\"
ExecStart=Tffd700${Te6edf3exec_cmdTffd700}
Ta5d6ffRestart=always
RestartSec=3
[Install]
WantedBy=default.target
EOFTa5d6ff"
Tff7b72}
api_status_is_okTff7b72(Tff7b72) Tff7b72{
Tffa657local Te6edf3schemeTff7b72=Ta5d6ff"Te6edf3$1Ta5d6ff"
Tffa657local Te6edf3hTff7b72=Ta5d6ff"Te6edf3$2Ta5d6ff"
Tffa657local Te6edf3pTff7b72=Ta5d6ff"Te6edf3$3Ta5d6ff"
Tffa657local Te6edf3urlTff7b72=Ta5d6ff"Tffd700${Te6edf3schemeTffd700}Ta5d6ff://Tffd700${Te6edf3hTffd700}Ta5d6ff:Tffd700${Te6edf3pTffd700}Ta5d6ff/api/v1/statusTa5d6ff"
Tff7b72if Tffa657command -v curl >/dev/null T79c0ff2>Tb4b4b4&T79c0ff1Tb4b4b4; Tff7b72then
Tff7b72if Tff7b72[Tff7b72[ Ta5d6ff"Te6edf3$schemeTa5d6ff" Tff7b72=Tff7b72= Ta5d6ff"https" Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
curl -fsS -k -m T79c0ff2 Ta5d6ff"Te6edf3$urlTa5d6ff" T79c0ff2>/dev/null Tb4b4b4| grep -qE Ta5d6ff'"status"[[:space:]]*:[[:space:]]*"ok"'
Tff7b72else
curl -fsS -m T79c0ff2 Ta5d6ff"Te6edf3$urlTa5d6ff" T79c0ff2>/dev/null Tb4b4b4| grep -qE Ta5d6ff'"status"[[:space:]]*:[[:space:]]*"ok"'
Tff7b72fi
Tff7b72elif Tffa657command -v wget >/dev/null T79c0ff2>Tb4b4b4&T79c0ff1Tb4b4b4; Tff7b72then
Tff7b72if Tff7b72[Tff7b72[ Ta5d6ff"Te6edf3$schemeTa5d6ff" Tff7b72=Tff7b72= Ta5d6ff"https" Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
wget -qO- -T T79c0ff2 --no-check-certificate Ta5d6ff"Te6edf3$urlTa5d6ff" T79c0ff2>/dev/null Tb4b4b4| grep -qE Ta5d6ff'"status"[[:space:]]*:[[:space:]]*"ok"'
Tff7b72else
wget -qO- -T T79c0ff2 Ta5d6ff"Te6edf3$urlTa5d6ff" T79c0ff2>/dev/null Tb4b4b4| grep -qE Ta5d6ff'"status"[[:space:]]*:[[:space:]]*"ok"'
Tff7b72fi
Tff7b72else
Tff7b72return T79c0ff1
Tff7b72fi
Tff7b72}
ensure_cosign_binaryTff7b72(Tff7b72) Tff7b72{
Tffa657local u ver bin_name expect act tmpd sumf base
Te6edf3uTff7b72=Ta5d6ff"Tff7b72$(uname -mTff7b72)Ta5d6ff"
Tff7b72case Ta5d6ff"Te6edf3$uTa5d6ff" Tff7b72in
x86_64Tff7b72) Te6edf3bin_nameTff7b72=Ta5d6ff"cosign-linux-amd64" Tb4b4b4;Tb4b4b4;
aarch64Tff7b72) Te6edf3bin_nameTff7b72=Ta5d6ff"cosign-linux-arm64" Tb4b4b4;Tb4b4b4;
arm64Tff7b72) Te6edf3bin_nameTff7b72=Ta5d6ff"cosign-linux-arm64" Tb4b4b4;Tb4b4b4;
armv7l Tb4b4b4| armv6lTff7b72) Te6edf3bin_nameTff7b72=Ta5d6ff"cosign-linux-arm" Tb4b4b4;Tb4b4b4;
*Tff7b72)
err Ta5d6ff"Ta5d6ffNo bootstrap cosign build for uname: Te6edf3$uTa5d6ff (install cosign, or skip attestation verify).Ta5d6ff"
Tff7b72return T79c0ff1
Tb4b4b4;Tb4b4b4;
Tff7b72esac
Te6edf3verTff7b72=Ta5d6ff"Tffd700${Te6edf3MESHCHATX_COSIGN_VERSIONTff7b72:-Te6edf33Tb4b4b4.0.6Tffd700}Ta5d6ff"
Te6edf3tmpdTff7b72=Ta5d6ff"Tffd700${Te6edf3TMPDIRTff7b72:-Tb4b4b4/tmpTffd700}Ta5d6ff/meshchatx-cosign-Tffd700${$Tffd700}Ta5d6ff"
mkdir -p Ta5d6ff"Te6edf3$tmpdTa5d6ff" Tff7b72|| Tff7b72return T79c0ff1
Te6edf3sumfTff7b72=Ta5d6ff"Tffd700${Te6edf3tmpdTffd700}Ta5d6ff/cosign_checksums.txtTa5d6ff"
Te6edf3baseTff7b72=Ta5d6ff"Ta5d6ffhttps://github.com/sigstore/cosign/releases/download/vTffd700${Te6edf3verTffd700}Ta5d6ff"
Tff7b72if ! curl -fsSL -m T79c0ff120 Ta5d6ff"Te6edf3$baseTa5d6ff/cosign_checksums.txtTa5d6ff" -o Ta5d6ff"Te6edf3$sumfTa5d6ff" Tffea00\
Tff7b72|| ! curl -fsSL -m T79c0ff120 Ta5d6ff"Te6edf3$baseTa5d6ff/Tffd700${Te6edf3bin_nameTffd700}Ta5d6ff" -o Ta5d6ff"Tffd700${Te6edf3tmpdTffd700}Ta5d6ff/cosignTa5d6ff"Tb4b4b4; Tff7b72then
rm -rf Ta5d6ff"Te6edf3$tmpdTa5d6ff"
err Ta5d6ff"Ta5d6ffCould not download cosign Tffd700${Te6edf3verTffd700}Ta5d6ff for verification.Ta5d6ff"
Tff7b72return T79c0ff1
Tff7b72fi
Te6edf3expectTff7b72=Ta5d6ff"Tff7b72$(awk -v Te6edf3bTff7b72=Ta5d6ff"Te6edf3$bin_nameTa5d6ff" Ta5d6ff'index($0, b) {print $1; exit}' Ta5d6ff"Te6edf3$sumfTa5d6ff" T79c0ff2>/dev/null Tff7b72|| Tffa657trueTff7b72)Ta5d6ff"
Te6edf3actTff7b72=Ta5d6ff"Tff7b72$(sha256sum Ta5d6ff"Tffd700${Te6edf3tmpdTffd700}Ta5d6ff/cosignTa5d6ff" Tb4b4b4| awk Ta5d6ff'{print $1}'Tff7b72)Ta5d6ff"
Tff7b72if Tff7b72[Tff7b72[ -z Ta5d6ff"Te6edf3$expectTa5d6ff" Tff7b72|| Ta5d6ff"Te6edf3$expectTa5d6ff" !Tff7b72= Ta5d6ff"Te6edf3$actTa5d6ff" Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
rm -rf Ta5d6ff"Te6edf3$tmpdTa5d6ff"
err Ta5d6ff"Ta5d6ffcosign binary SHA256 mismatch (expected from checksums: Tffd700${Te6edf3expectTff7b72:-Te6edf3missingTffd700}Ta5d6ff).Ta5d6ff"
Tff7b72return T79c0ff1
Tff7b72fi
chmod T79c0ff755 Ta5d6ff"Tffd700${Te6edf3tmpdTffd700}Ta5d6ff/cosignTa5d6ff"
Tffa657echo Ta5d6ff"Tffd700${Te6edf3tmpdTffd700}Ta5d6ff/cosignTa5d6ff"
Tff7b72}
try_verify_and_localize_wheelTff7b72(Tff7b72) Tff7b72{
Te6edf3MESHCHATX_WHEEL_FILETff7b72=Ta5d6ff""
Tffa657local Te6edf3src_urlTff7b72=Ta5d6ff"Tffd700${Te6edf31Tff7b72:-Tffd700}Ta5d6ff"
Tffa657local Te6edf3bundle_urlTff7b72=Ta5d6ff"Tffd700${Te6edf3src_urlTffd700}Ta5d6ff.cosign.bundleTa5d6ff"
Tffa657local code cbin kf wf bf
Te6edf3MESHCHATX_COSIGN_PUB_URLTff7b72=Ta5d6ff"Tffd700${Te6edf3MESHCHATX_COSIGN_PUB_URLTff7b72:-Te6edf3httpsTb4b4b4:Tb4b4b4//github.com/Quad4-Software/MeshChatX/raw/branch/master/cosign.pubTffd700}Ta5d6ff"
Tff7b72if ! Te6edf3codeTff7b72=Ta5d6ff"Tff7b72$(curl -fsS -o /dev/null -w Ta5d6ff'%{http_code}' -I -L -m T79c0ff30 Ta5d6ff"Te6edf3$bundle_urlTa5d6ff" T79c0ff2>/dev/nullTff7b72)Ta5d6ff"Tb4b4b4; Tff7b72then
Te6edf3codeTff7b72=Ta5d6ff"000"
Tff7b72fi
Tff7b72[Tff7b72[ Ta5d6ff"Te6edf3$codeTa5d6ff" Tff7b72=Tff7b72= Ta5d6ff"200" Tff7b72]Tff7b72] Tff7b72|| Tff7b72return T79c0ff1
Tff7b72if ! prompt_yes_no Ta5d6ff"A cosign attestation exists for this wheel. Verify with cosign (uses PATH binary, or one-time download to /tmp, not a system package)?" Ta5d6ff"y"Tb4b4b4; Tff7b72then
Tff7b72return T79c0ff1
Tff7b72fi
Te6edf3wfTff7b72=Ta5d6ff"Tff7b72$(mktemp Ta5d6ff"Tffd700${Te6edf3TMPDIRTff7b72:-Tb4b4b4/tmpTffd700}Ta5d6ff/meshchatx.XXXXXX.whlTa5d6ff"Tff7b72)Ta5d6ff" Tff7b72|| Tff7b72return T79c0ff1
Te6edf3bfTff7b72=Ta5d6ff"Tff7b72$(mktemp Ta5d6ff"Tffd700${Te6edf3TMPDIRTff7b72:-Tb4b4b4/tmpTffd700}Ta5d6ff/meshchatx.XXXXXX.cosign.bundleTa5d6ff"Tff7b72)Ta5d6ff" Tff7b72|| Tff7b72{
rm -f Ta5d6ff"Te6edf3$wfTa5d6ff"
Tff7b72return T79c0ff1
Tff7b72}
Te6edf3kfTff7b72=Ta5d6ff"Tff7b72$(mktemp Ta5d6ff"Tffd700${Te6edf3TMPDIRTff7b72:-Tb4b4b4/tmpTffd700}Ta5d6ff/meshchatx.XXXXXX.pubTa5d6ff"Tff7b72)Ta5d6ff" Tff7b72|| Tff7b72{
rm -f Ta5d6ff"Te6edf3$wfTa5d6ff" Ta5d6ff"Te6edf3$bfTa5d6ff"
Tff7b72return T79c0ff1
Tff7b72}
Tff7b72if ! curl -fsSL -m T79c0ff600 Ta5d6ff"Te6edf3$src_urlTa5d6ff" -o Ta5d6ff"Te6edf3$wfTa5d6ff" Tff7b72|| ! curl -fsSL -m T79c0ff120 Ta5d6ff"Te6edf3$bundle_urlTa5d6ff" -o Ta5d6ff"Te6edf3$bfTa5d6ff" Tff7b72|| ! curl -fsSL -m T79c0ff60 Ta5d6ff"Te6edf3$MESHCHATX_COSIGN_PUB_URLTa5d6ff" -o Ta5d6ff"Te6edf3$kfTa5d6ff"Tb4b4b4; Tff7b72then
err Ta5d6ff"Failed to download wheel, bundle, or cosign.pub."
rm -f Ta5d6ff"Te6edf3$wfTa5d6ff" Ta5d6ff"Te6edf3$bfTa5d6ff" Ta5d6ff"Te6edf3$kfTa5d6ff"
Tff7b72return T79c0ff1
Tff7b72fi
Tff7b72if Tffa657command -v cosign >/dev/null T79c0ff2>Tb4b4b4&T79c0ff1Tb4b4b4; Tff7b72then
Te6edf3cbinTff7b72=Ta5d6ff"Tff7b72$(Tffa657command -v cosignTff7b72)Ta5d6ff"
Tff7b72elif ! Te6edf3cbinTff7b72=Ta5d6ff"Tff7b72$(ensure_cosign_binaryTff7b72)Ta5d6ff"Tb4b4b4; Tff7b72then
err Ta5d6ff"Set cosign on PATH, or use a build arch supported by the Sigstore binary."
rm -f Ta5d6ff"Te6edf3$wfTa5d6ff" Ta5d6ff"Te6edf3$bfTa5d6ff" Ta5d6ff"Te6edf3$kfTa5d6ff"
Tff7b72return T79c0ff1
Tff7b72fi
Tff7b72if ! Ta5d6ff"Te6edf3$cbinTa5d6ff" verify-blob-attestation --key Ta5d6ff"Te6edf3$kfTa5d6ff" --bundle Ta5d6ff"Te6edf3$bfTa5d6ff" --type slsaprovenance1 Tffea00\
--insecure-ignore-tlogTff7b72=Tffa657true Ta5d6ff"Te6edf3$wfTa5d6ff"Tb4b4b4; Tff7b72then
err Ta5d6ff"cosign attestation verification failed for the downloaded wheel."
rm -f Ta5d6ff"Te6edf3$wfTa5d6ff" Ta5d6ff"Te6edf3$bfTa5d6ff" Ta5d6ff"Te6edf3$kfTa5d6ff"
Tffa657exit T79c0ff1
Tff7b72fi
rm -f Ta5d6ff"Te6edf3$bfTa5d6ff" Ta5d6ff"Te6edf3$kfTa5d6ff"
Te6edf3MESHCHATX_WHEEL_FILETff7b72=Ta5d6ff"Te6edf3$wfTa5d6ff"
ok Ta5d6ff"Ta5d6ffcosign attestation OK (SLSA provenance v1). Installing verified wheel from Tffd700${Te6edf3wfTffd700}Ta5d6ff.Ta5d6ff"
Tff7b72return T79c0ff0
Tff7b72}
handle_service_start_failureTff7b72(Tff7b72) Tff7b72{
Tffa657local Te6edf3modeTff7b72=Ta5d6ff"Te6edf3$1Ta5d6ff"
Tffa657local Te6edf3reasonTff7b72=Ta5d6ff"Te6edf3$2Ta5d6ff"
err Ta5d6ff"Te6edf3$reasonTa5d6ff"
warn Ta5d6ff"Service startup failed. Recent logs:"
Tff7b72if Tff7b72[Tff7b72[ Ta5d6ff"Te6edf3$modeTa5d6ff" Tff7b72=Tff7b72= Ta5d6ff"system" Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
sudo journalctl -u meshchatx.service -n T79c0ff200 --no-pager Tff7b72|| Tffa657true
sudo systemctl stop meshchatx.service Tff7b72|| Tffa657true
sudo systemctl reset-failed meshchatx.service Tff7b72|| Tffa657true
Tff7b72else
run_as_user Ta5d6ff"journalctl --user -u meshchatx.service -n 200 --no-pager" Tff7b72|| Tffa657true
run_as_user Ta5d6ff"systemctl --user stop meshchatx.service || true"
run_as_user Ta5d6ff"systemctl --user reset-failed meshchatx.service || true"
Tff7b72fi
err Ta5d6ff"Service was stopped/reset. Fix config and run installer again."
Tffa657exit T79c0ff1
Tff7b72}
verify_service_startedTff7b72(Tff7b72) Tff7b72{
Tffa657local Te6edf3modeTff7b72=Ta5d6ff"Te6edf3$1Ta5d6ff"
Tffa657local Te6edf3probe_hostTff7b72=Ta5d6ff"Te6edf3$2Ta5d6ff"
Tffa657local Te6edf3probe_portTff7b72=Ta5d6ff"Te6edf3$3Ta5d6ff"
Tffa657local Te6edf3https_enabledTff7b72=Ta5d6ff"Te6edf3$4Ta5d6ff"
Tffa657local Te6edf3triesTff7b72=T79c0ff40
Tffa657local Te6edf3log_cmdTff7b72=Ta5d6ff""
Tffa657local Te6edf3stop_cmdTff7b72=Ta5d6ff""
Tffa657local Te6edf3schemeTff7b72=Ta5d6ff"https"
Tff7b72if Tff7b72[Tff7b72[ Ta5d6ff"Te6edf3$https_enabledTa5d6ff" Tff7b72=Tff7b72= Ta5d6ff"no" Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
Te6edf3schemeTff7b72=Ta5d6ff"http"
Tff7b72fi
Tff7b72if Tff7b72[Tff7b72[ Ta5d6ff"Te6edf3$modeTa5d6ff" Tff7b72=Tff7b72= Ta5d6ff"system" Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
Te6edf3log_cmdTff7b72=Ta5d6ff"sudo journalctl -u meshchatx.service -n 200 --no-pager"
Te6edf3stop_cmdTff7b72=Ta5d6ff"sudo systemctl stop meshchatx.service; sudo systemctl reset-failed meshchatx.service"
Tff7b72else
Te6edf3log_cmdTff7b72=Ta5d6ff"journalctl --user -u meshchatx.service -n 200 --no-pager"
Te6edf3stop_cmdTff7b72=Ta5d6ff"systemctl --user stop meshchatx.service || true; systemctl --user reset-failed meshchatx.service || true"
Tff7b72fi
note Ta5d6ff"Ta5d6ffVerifying service startup via Tffd700${Te6edf3schemeTffd700}Ta5d6ff://Tffd700${Te6edf3probe_hostTffd700}Ta5d6ff:Tffd700${Te6edf3probe_portTffd700}Ta5d6ff/api/v1/status ...Ta5d6ff"
Tff7b72for _ Tff7b72in Tff7b72$(seq T79c0ff1 Ta5d6ff"Te6edf3$triesTa5d6ff"Tff7b72)Tb4b4b4; Tff7b72do
Tff7b72if api_status_is_ok Ta5d6ff"Te6edf3$schemeTa5d6ff" Ta5d6ff"Te6edf3$probe_hostTa5d6ff" Ta5d6ff"Te6edf3$probe_portTa5d6ff"Tb4b4b4; Tff7b72then
ok Ta5d6ff"Service started successfully (status endpoint is healthy)."
Tff7b72return T79c0ff0
Tff7b72fi
sleep T79c0ff1
Tff7b72done
err Ta5d6ff"Service did not pass status endpoint health check."
warn Ta5d6ff"Recent logs:"
Tff7b72if Tff7b72[Tff7b72[ Ta5d6ff"Te6edf3$modeTa5d6ff" Tff7b72=Tff7b72= Ta5d6ff"system" Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
sudo journalctl -u meshchatx.service -n T79c0ff200 --no-pager Tff7b72|| Tffa657true
Tffa657eval Ta5d6ff"Te6edf3$stop_cmdTa5d6ff"
Tff7b72else
run_as_user Ta5d6ff"Te6edf3$log_cmdTa5d6ff" Tff7b72|| Tffa657true
run_as_user Ta5d6ff"Te6edf3$stop_cmdTa5d6ff"
Tff7b72fi
err Ta5d6ff"Service was stopped to prevent restart loops."
Tff7b72return T79c0ff1
Tff7b72}
mainTff7b72(Tff7b72) Tff7b72{
note Ta5d6ff"MeshChatX Raspberry Pi Interactive Installer"
Tffa657echo Ta5d6ff"Ta5d6ffDetected user: Tffd700${Te6edf3RUN_USERTffd700}Ta5d6ff (group: Tffd700${Te6edf3RUN_GROUPTffd700}Ta5d6ff)Ta5d6ff"
Tffa657local arch
Te6edf3archTff7b72=Ta5d6ff"Tff7b72$(detect_archTff7b72)Ta5d6ff"
Tffa657echo Ta5d6ff"Ta5d6ffDetected architecture: Tffd700${Te6edf3archTffd700}Ta5d6ff"
Tff7b72if is_supported_rpi_arch Ta5d6ff"Te6edf3$archTa5d6ff"Tb4b4b4; Tff7b72then
ok Ta5d6ff"Ta5d6ffDetected Raspberry Pi ARM architecture (Tffd700${Te6edf3archTffd700}Ta5d6ff).Ta5d6ff"
Tff7b72else
warn Ta5d6ff"Ta5d6ffDetected non-RPi arch (Tffd700${Te6edf3archTffd700}Ta5d6ff). Script can still run, but this guide targets Raspberry Pi ARM.Ta5d6ff"
Tff7b72fi
note Ta5d6ff"The wheel is py3-none-any, so it is architecture-independent (32-bit and 64-bit ARM are supported)."
Tffa657echo
Tff7b72if prompt_yes_no Ta5d6ff"Do you want to install espeak-ng?" Ta5d6ff"y"Tb4b4b4; Tff7b72then
note Ta5d6ff"Installing espeak-ng (best effort)..."
Tff7b72if ! install_package_if_possible Ta5d6ff"espeak-ng"Tb4b4b4; Tff7b72then
warn Ta5d6ff"Could not install espeak-ng automatically; continuing."
Tff7b72fi
Tff7b72else
note Ta5d6ff"Skipping espeak-ng installation."
Tff7b72fi
Tffa657local Te6edf3method_choiceTff7b72=Ta5d6ff""
Tff7b72while Tff7b72[Tff7b72[ Ta5d6ff"Te6edf3$method_choiceTa5d6ff" !Tff7b72= Ta5d6ff"1" Tff7b72&& Ta5d6ff"Te6edf3$method_choiceTa5d6ff" !Tff7b72= Ta5d6ff"2" Tff7b72]Tff7b72]Tb4b4b4; Tff7b72do
Tffa657echo
Tffa657echo Ta5d6ff"Choose installation method:"
Tffa657echo Ta5d6ff" 1) pipx (recommended)"
Tffa657echo Ta5d6ff" 2) venv + pip"
Tffa657read -r -p Ta5d6ff"Selection [1/2]: " method_choice
Tff7b72if Tff7b72[Tff7b72[ -z Ta5d6ff"Te6edf3$method_choiceTa5d6ff" Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
Te6edf3method_choiceTff7b72=Ta5d6ff"1"
Tff7b72fi
Tff7b72done
Tffa657local Te6edf3wheel_urlTff7b72=Ta5d6ff""
Tffa657local -a Te6edf3rel_linesTff7b72=Tff7b72(Tff7b72)
note Ta5d6ff"Fetching release list from Gitea (RSS)..."
Tff7b72if mapfile -t rel_lines < <Tff7b72(discover_release_wheels T79c0ff2>/dev/nullTff7b72) Tff7b72&& Tff7b72[Tff7b72[ Tffd700${#Te6edf3rel_linesTb4b4b4[@]Tffd700} -ge T79c0ff4 Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
Te6edf3wheel_urlTff7b72=Ta5d6ff"Tff7b72$(prompt_wheel_source Ta5d6ff"Tffd700${Te6edf3rel_linesTb4b4b4[0]Tff7b72:-Tffd700}Ta5d6ff" Ta5d6ff"Tffd700${Te6edf3rel_linesTb4b4b4[1]Tff7b72:-Tffd700}Ta5d6ff" Ta5d6ff"Tffd700${Te6edf3rel_linesTb4b4b4[2]Tff7b72:-Tffd700}Ta5d6ff" Ta5d6ff"Tffd700${Te6edf3rel_linesTb4b4b4[3]Tff7b72:-Tffd700}Ta5d6ff"Tff7b72)Ta5d6ff"
Tff7b72else
warn Ta5d6ff"Could not use the release feed (set MESHCHATX_RELEASES_RSS or check network)."
Te6edf3wheel_urlTff7b72=Ta5d6ff"Tff7b72$(prompt_default Ta5d6ff"Wheel URL" Ta5d6ff""Tff7b72)Ta5d6ff"
Tff7b72if Tff7b72[Tff7b72[ -z Ta5d6ff"Te6edf3$wheel_urlTa5d6ff" Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
err Ta5d6ff"Aborted: need a wheel URL."
Tffa657exit T79c0ff1
Tff7b72fi
Tff7b72fi
ok Ta5d6ff"Ta5d6ffSelected wheel: Tffd700${Te6edf3wheel_urlTffd700}Ta5d6ff"
Tffa657local Te6edf3wheel_install_targetTff7b72=Ta5d6ff"Te6edf3$wheel_urlTa5d6ff"
Te6edf3MESHCHATX_WHEEL_FILETff7b72=Ta5d6ff""
Tff7b72if try_verify_and_localize_wheel Ta5d6ff"Te6edf3$wheel_urlTa5d6ff"Tb4b4b4; Tff7b72then
Te6edf3wheel_install_targetTff7b72=Ta5d6ff"Tffd700${Te6edf3MESHCHATX_WHEEL_FILETffd700}Ta5d6ff"
Tff7b72fi
Tffa657local install_root
Te6edf3install_rootTff7b72=Ta5d6ff"Tff7b72$(prompt_default Ta5d6ff"Install root directory" Ta5d6ff"Tffd700${Te6edf3USER_HOMETffd700}Ta5d6ff/meshchatxTa5d6ff"Tff7b72)Ta5d6ff"
Tffa657local storage_dir
Te6edf3storage_dirTff7b72=Ta5d6ff"Tff7b72$(prompt_default Ta5d6ff"Storage directory" Ta5d6ff"Tffd700${Te6edf3install_rootTffd700}Ta5d6ff/storageTa5d6ff"Tff7b72)Ta5d6ff"
Tffa657local rns_dir
Te6edf3rns_dirTff7b72=Ta5d6ff"Tff7b72$(prompt_default Ta5d6ff"Reticulum config directory" Ta5d6ff"Tffd700${Te6edf3USER_HOMETffd700}Ta5d6ff/.reticulumTa5d6ff"Tff7b72)Ta5d6ff"
Tffa657local bind_host
Te6edf3bind_hostTff7b72=Ta5d6ff"Tff7b72$(prompt_default Ta5d6ff"Bind IP/host" Ta5d6ff"0.0.0.0"Tff7b72)Ta5d6ff"
Tffa657local bind_port
Te6edf3bind_portTff7b72=Ta5d6ff"Tff7b72$(prompt_default Ta5d6ff"Bind port" Ta5d6ff"8000"Tff7b72)Ta5d6ff"
Tff7b72if check_port_available Ta5d6ff"Te6edf3$bind_portTa5d6ff"Tb4b4b4; Tff7b72then
ok Ta5d6ff"Ta5d6ffPort Tffd700${Te6edf3bind_portTffd700}Ta5d6ff is available on Tffd700${Te6edf3bind_hostTffd700}Ta5d6ff.Ta5d6ff"
Tff7b72else
warn Ta5d6ff"Ta5d6ffPort Tffd700${Te6edf3bind_portTffd700}Ta5d6ff appears to be in use on Tffd700${Te6edf3bind_hostTffd700}Ta5d6ff.Ta5d6ff"
Tff7b72if ! prompt_yes_no Ta5d6ff"Continue anyway?" Ta5d6ff"n"Tb4b4b4; Tff7b72then
err Ta5d6ff"Aborted."
Tffa657exit T79c0ff1
Tff7b72fi
Tff7b72fi
Tffa657local Te6edf3https_enabledTff7b72=Ta5d6ff"yes"
Tff7b72if ! prompt_yes_no Ta5d6ff"Enable HTTPS?" Ta5d6ff"y"Tb4b4b4; Tff7b72then
Te6edf3https_enabledTff7b72=Ta5d6ff"no"
Tff7b72fi
Tffa657local Te6edf3service_modeTff7b72=Ta5d6ff"none"
Tff7b72if prompt_yes_no Ta5d6ff"Do you want to configure a systemd service?" Ta5d6ff"y"Tb4b4b4; Tff7b72then
Te6edf3service_modeTff7b72=Ta5d6ff""
Tff7b72while Tff7b72[Tff7b72[ Ta5d6ff"Te6edf3$service_modeTa5d6ff" !Tff7b72= Ta5d6ff"system" Tff7b72&& Ta5d6ff"Te6edf3$service_modeTa5d6ff" !Tff7b72= Ta5d6ff"user" Tff7b72]Tff7b72]Tb4b4b4; Tff7b72do
Te6edf3service_modeTff7b72=Ta5d6ff"Tff7b72$(prompt_default Ta5d6ff"Service mode (system/user)" Ta5d6ff"system"Tff7b72)Ta5d6ff"
Tff7b72done
Tff7b72fi
Tffa657local Te6edf3no_https_flagTff7b72=Ta5d6ff""
Tff7b72if Tff7b72[Tff7b72[ Ta5d6ff"Te6edf3$https_enabledTa5d6ff" Tff7b72=Tff7b72= Ta5d6ff"no" Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
Te6edf3no_https_flagTff7b72=Ta5d6ff" --no-https"
Tff7b72fi
Tffa657local Te6edf3probe_hostTff7b72=Ta5d6ff"Te6edf3$bind_hostTa5d6ff"
Tff7b72if Tff7b72[Tff7b72[ Ta5d6ff"Te6edf3$bind_hostTa5d6ff" Tff7b72=Tff7b72= Ta5d6ff"0.0.0.0" Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
Te6edf3probe_hostTff7b72=Ta5d6ff"127.0.0.1"
Tff7b72elif Tff7b72[Tff7b72[ Ta5d6ff"Te6edf3$bind_hostTa5d6ff" Tff7b72=Tff7b72= Ta5d6ff"::" Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
Te6edf3probe_hostTff7b72=Ta5d6ff"::1"
Tff7b72fi
note Ta5d6ff"Preparing directories..."
Tff7b72if Tff7b72[Tff7b72[ Ta5d6ff"Te6edf3$EUIDTa5d6ff" -eq T79c0ff0 Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
install -d -m T79c0ff755 -o Ta5d6ff"Te6edf3$RUN_USERTa5d6ff" -g Ta5d6ff"Te6edf3$RUN_GROUPTa5d6ff" Ta5d6ff"Te6edf3$install_rootTa5d6ff" Ta5d6ff"Te6edf3$storage_dirTa5d6ff" Ta5d6ff"Te6edf3$rns_dirTa5d6ff"
Tff7b72else
mkdir -p Ta5d6ff"Te6edf3$install_rootTa5d6ff" Ta5d6ff"Te6edf3$storage_dirTa5d6ff" Ta5d6ff"Te6edf3$rns_dirTa5d6ff"
Tff7b72if Tffa657command -v sudo >/dev/null T79c0ff2>Tb4b4b4&T79c0ff1Tb4b4b4; Tff7b72then
sudo install -d -m T79c0ff755 -o Ta5d6ff"Te6edf3$RUN_USERTa5d6ff" -g Ta5d6ff"Te6edf3$RUN_GROUPTa5d6ff" Ta5d6ff"Te6edf3$install_rootTa5d6ff" Ta5d6ff"Te6edf3$storage_dirTa5d6ff" Ta5d6ff"Te6edf3$rns_dirTa5d6ff" >/dev/null T79c0ff2>Tb4b4b4&T79c0ff1 Tff7b72|| Tffa657true
Tff7b72fi
Tff7b72fi
Tffa657local Te6edf3bin_pathTff7b72=Ta5d6ff""
Tffa657local Te6edf3venv_pathTff7b72=Ta5d6ff"Tffd700${Te6edf3install_rootTffd700}Ta5d6ff/.venvTa5d6ff"
Tff7b72if Tff7b72[Tff7b72[ Ta5d6ff"Te6edf3$method_choiceTa5d6ff" Tff7b72=Tff7b72= Ta5d6ff"1" Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
Tff7b72if ! Tffa657command -v pipx >/dev/null T79c0ff2>Tb4b4b4&T79c0ff1Tb4b4b4; Tff7b72then
err Ta5d6ff"pipx not found. Install pipx first or choose venv method."
Tffa657exit T79c0ff1
Tff7b72fi
note Ta5d6ff"Installing MeshChatX via pipx..."
run_as_user Ta5d6ff"pipx ensurepath >/dev/null 2>&1 || true"
run_as_user Ta5d6ff"Ta5d6ffpipx install --force 'Tffd700${Te6edf3wheel_install_targetTffd700}Ta5d6ff'Ta5d6ff"
run_as_user Ta5d6ff"pipx inject reticulum-meshchatx packaging >/dev/null 2>&1 || true"
Te6edf3bin_pathTff7b72=Ta5d6ff"Tffd700${Te6edf3USER_HOMETffd700}Ta5d6ff/.local/bin/meshchatxTa5d6ff"
Tff7b72else
note Ta5d6ff"Installing MeshChatX via venv + pip..."
run_as_user Ta5d6ff"Ta5d6ffpython3 -m venv 'Tffd700${Te6edf3venv_pathTffd700}Ta5d6ff'Ta5d6ff"
run_as_user Ta5d6ff"Ta5d6ff'Tffd700${Te6edf3venv_pathTffd700}Ta5d6ff/bin/python' -m pip install --upgrade pipTa5d6ff"
run_as_user Ta5d6ff"Ta5d6ff'Tffd700${Te6edf3venv_pathTffd700}Ta5d6ff/bin/python' -m pip install 'Tffd700${Te6edf3wheel_install_targetTffd700}Ta5d6ff'Ta5d6ff"
run_as_user Ta5d6ff"Ta5d6ff'Tffd700${Te6edf3venv_pathTffd700}Ta5d6ff/bin/python' -m pip install packagingTa5d6ff"
Te6edf3bin_pathTff7b72=Ta5d6ff"Tffd700${Te6edf3venv_pathTffd700}Ta5d6ff/bin/meshchatxTa5d6ff"
Tff7b72fi
Tff7b72if Tff7b72[Tff7b72[ ! -x Ta5d6ff"Te6edf3$bin_pathTa5d6ff" Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
err Ta5d6ff"Ta5d6ffmeshchatx binary not found at: Te6edf3$bin_pathTa5d6ff"
Tffa657exit T79c0ff1
Tff7b72fi
Tffa657local Te6edf3exec_cmdTff7b72=Ta5d6ff"Tffd700${Te6edf3bin_pathTffd700}Ta5d6ff --headless --host Tffd700${Te6edf3bind_hostTffd700}Ta5d6ff --port Tffd700${Te6edf3bind_portTffd700}Ta5d6ff --storage-dir Tffd700${Te6edf3storage_dirTffd700}Ta5d6ff --reticulum-config-dir Tffd700${Te6edf3rns_dirTffd700}Tffd700${Te6edf3no_https_flagTffd700}Ta5d6ff"
Tffa657local Te6edf3path_envTff7b72=Ta5d6ff"Tffd700${Te6edf3venv_pathTffd700}Ta5d6ff/bin:Tffd700${Te6edf3USER_HOMETffd700}Ta5d6ff/.local/bin:/usr/bin:/binTa5d6ff"
Tff7b72if Tff7b72[Tff7b72[ Ta5d6ff"Te6edf3$service_modeTa5d6ff" Tff7b72=Tff7b72= Ta5d6ff"none" Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
ok Ta5d6ff"Install complete."
Tffa657echo
Tffa657echo Ta5d6ff"Run command:"
Tffa657echo Ta5d6ff"Ta5d6ff Tffd700${Te6edf3exec_cmdTffd700}Ta5d6ff"
Tffa657exit T79c0ff0
Tff7b72fi
Tff7b72if Tff7b72[Tff7b72[ Ta5d6ff"Te6edf3$service_modeTa5d6ff" Tff7b72=Tff7b72= Ta5d6ff"system" Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
note Ta5d6ff"Creating system service..."
write_system_service Ta5d6ff"Te6edf3$exec_cmdTa5d6ff" Ta5d6ff"Te6edf3$install_rootTa5d6ff" Ta5d6ff"Te6edf3$path_envTa5d6ff"
Tff7b72if Tff7b72[Tff7b72[ Ta5d6ff"Te6edf3$EUIDTa5d6ff" -eq T79c0ff0 Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
Tff7b72if ! systemctl daemon-reloadTb4b4b4; Tff7b72then
handle_service_start_failure Ta5d6ff"system" Ta5d6ff"systemctl daemon-reload failed."
Tff7b72fi
Tff7b72if ! systemctl Tffa657enable --now meshchatx.serviceTb4b4b4; Tff7b72then
handle_service_start_failure Ta5d6ff"system" Ta5d6ff"systemctl enable/start failed."
Tff7b72fi
Tff7b72else
Tff7b72if ! sudo systemctl daemon-reloadTb4b4b4; Tff7b72then
handle_service_start_failure Ta5d6ff"system" Ta5d6ff"sudo systemctl daemon-reload failed."
Tff7b72fi
Tff7b72if ! sudo systemctl Tffa657enable --now meshchatx.serviceTb4b4b4; Tff7b72then
handle_service_start_failure Ta5d6ff"system" Ta5d6ff"sudo systemctl enable/start failed."
Tff7b72fi
Tff7b72fi
Tff7b72if ! verify_service_started Ta5d6ff"system" Ta5d6ff"Te6edf3$probe_hostTa5d6ff" Ta5d6ff"Te6edf3$bind_portTa5d6ff" Ta5d6ff"Te6edf3$https_enabledTa5d6ff"Tb4b4b4; Tff7b72then
Tffa657exit T79c0ff1
Tff7b72fi
ok Ta5d6ff"System service is enabled and running."
Tff7b72else
Tff7b72if Tff7b72[Tff7b72[ Ta5d6ff"Te6edf3$service_modeTa5d6ff" Tff7b72=Tff7b72= Ta5d6ff"user" Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
note Ta5d6ff"Creating user service..."
write_user_service Ta5d6ff"Te6edf3$exec_cmdTa5d6ff" Ta5d6ff"Te6edf3$install_rootTa5d6ff" Ta5d6ff"Te6edf3$path_envTa5d6ff"
Tff7b72if ! run_as_user Ta5d6ff"systemctl --user daemon-reload"Tb4b4b4; Tff7b72then
handle_service_start_failure Ta5d6ff"user" Ta5d6ff"systemctl --user daemon-reload failed."
Tff7b72fi
Tff7b72if ! run_as_user Ta5d6ff"systemctl --user enable --now meshchatx.service"Tb4b4b4; Tff7b72then
handle_service_start_failure Ta5d6ff"user" Ta5d6ff"systemctl --user enable/start failed."
Tff7b72fi
Tff7b72if ! verify_service_started Ta5d6ff"user" Ta5d6ff"Te6edf3$probe_hostTa5d6ff" Ta5d6ff"Te6edf3$bind_portTa5d6ff" Ta5d6ff"Te6edf3$https_enabledTa5d6ff"Tb4b4b4; Tff7b72then
Tffa657exit T79c0ff1
Tff7b72fi
ok Ta5d6ff"User service is enabled and running."
Tff7b72fi
Tff7b72fi
Tffa657echo
Tffa657echo Ta5d6ff"Web UI:"
Tffa657echo Ta5d6ff"Ta5d6ff https://Tffd700${Te6edf3bind_hostTffd700}Ta5d6ff:Tffd700${Te6edf3bind_portTffd700}Ta5d6ff"
Tff7b72if Tff7b72[Tff7b72[ Ta5d6ff"Te6edf3$https_enabledTa5d6ff" Tff7b72=Tff7b72= Ta5d6ff"no" Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
Tffa657echo Ta5d6ff" (HTTP mode enabled)"
Tff7b72fi
Tff7b72}
main Ta5d6ff"Te6edf3$@Ta5d6ff"
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────